home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 July: Mac OS SDK / Dev.CD Jul 96 SDK / Dev.CD Jul 96 SDK1.toast / Development Kits (Disc 1) / OpenDoc Development Framework / ODFDev / Form / Sources / Frame.cpp < prev    next >
Encoding:
Text File  |  1996-04-25  |  17.9 KB  |  581 lines  |  [TEXT/MPS ]

  1. //========================================================================================
  2. //
  3. //    File:                Frame.cpp
  4. //    Release Version:    $ ODF 1 $
  5. //
  6. //    Author:                Laurent Delamare
  7. //
  8. //    Copyright:            (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved.
  9. //
  10. //========================================================================================
  11.  
  12. #include "Form.hpp"
  13.  
  14. #ifndef FRAME_H
  15. #include "Frame.h"
  16. #endif
  17.  
  18. #ifndef DIALOG_H
  19. #include "Dialog.h"
  20. #endif
  21.  
  22. #ifndef PART_H
  23. #include "Part.h"
  24. #endif
  25.  
  26. #ifndef FORMVIEW_H
  27. #include "FormView.h"
  28. #endif
  29.  
  30. #ifndef EDITCMD_H
  31. #include "EditCmd.h"
  32. #endif
  33.  
  34. // ----- Framework Layer -----
  35.  
  36. #ifndef FWBUTTON_H
  37. #include "FWButton.h"
  38. #endif
  39.  
  40. #ifndef FWTABBER_H
  41. #include "FWTabber.h"
  42. #endif
  43.  
  44. #ifndef FWIDLE_H
  45. #include "FWIdle.h"
  46. #endif
  47.  
  48. #ifndef FWUTIL_H
  49. #include "FWUtil.h"
  50. #endif
  51.  
  52. #ifndef FWSCROLR_H
  53. #include "FWScrolr.h"
  54. #endif
  55.  
  56. #ifndef FWSCLBAR_H
  57. #include "FWSclBar.h"
  58. #endif
  59.  
  60. #ifndef FWGROWBX_H
  61. #include "FWGrowBx.h"
  62. #endif
  63.  
  64. #ifndef FWBUTTON_H
  65. #include "FWButton.h"
  66. #endif
  67.  
  68. #ifndef FWPOPUP_H
  69. #include "FWPopup.h"
  70. #endif
  71.  
  72. #ifndef FWCONTXT_H
  73. #include "FWContxt.h"
  74. #endif
  75.  
  76. #ifndef FWPRHDLR_H
  77. #include "FWPrHdlr.h"
  78. #endif
  79.  
  80. // ----- OS Layer -----
  81.  
  82. #ifndef FWMENU_H
  83. #include "FWMenu.h"
  84. #endif
  85.  
  86. #ifndef FWEVENT_H
  87. #include "FWEvent.h"
  88. #endif
  89.  
  90. #ifndef FWALERT_H
  91. #include "FWAlert.h"
  92. #endif
  93.  
  94. #ifndef SLMixOS_H
  95. #include "SLMixOS.h"
  96. #endif
  97.  
  98. #ifndef FWCFMRES_H
  99. #include "FWCFMRes.h"
  100. #endif
  101.  
  102. // ----- Graphic Includes -----
  103.  
  104. #ifndef FWRECT_H
  105. #include "FWRect.h"
  106. #endif
  107.  
  108. #ifndef FWTXTBOX_H
  109. #include "FWTxtBox.h"
  110. #endif
  111.  
  112. #ifndef FWRECSHP_H
  113. #include "FWRecShp.h"
  114. #endif
  115.  
  116. #ifndef FWPICSHP_H
  117. #include "FWPicShp.h"        
  118. #endif
  119.  
  120. // ----- Foundation Layer -----
  121.  
  122. #ifndef FWNOTIFN_H
  123. #include "FWNotifn.h"
  124. #endif
  125.  
  126. #ifndef FWNOTDEF_H
  127. #include "FWNotDef.h"
  128. #endif
  129.  
  130. //========================================================================================
  131. // Runtime Informations
  132. //========================================================================================
  133.  
  134. #ifdef FW_BUILD_MAC
  135. #pragma segment odfform
  136. #endif
  137.  
  138. FW_DEFINE_AUTO(CFormFrame)
  139. FW_DEFINE_CLASS_M2(CFormFrame, FW_CFrame, FW_MReceiver)
  140.  
  141. //========================================================================================
  142. // CFormFrame class
  143. //========================================================================================
  144.  
  145. //----------------------------------------------------------------------------------------
  146. // CFormFrame::CFormFrame
  147. //----------------------------------------------------------------------------------------
  148. CFormFrame::CFormFrame(Environment* ev, ODFrame* odFrame, FW_CPresentation* presentation, CFormPart* part)
  149.     : FW_CFrame(ev, odFrame, presentation, part),
  150.     fFormPart(part),
  151.     fIdler(NULL),
  152.     fViewTabber(NULL)
  153. {
  154.     // We must create an idler to see the caret blink in the text-edit views
  155.     fIdler = FW_NEW(FW_CIdler, (this, 15));
  156.     fIdler->RegisterIdle(ev);                
  157. }
  158.  
  159. //----------------------------------------------------------------------------------------
  160. // CFormFrame::~CFormFrame
  161. //----------------------------------------------------------------------------------------
  162.  
  163. CFormFrame::~CFormFrame()
  164. {
  165.     delete fIdler;
  166.     
  167.     // fViewTabber is deleted automatically because it's an attached event handler
  168. }
  169.  
  170. //----------------------------------------------------------------------------------------
  171. //    CFormFrame::PostCreateViewFromStream
  172. //----------------------------------------------------------------------------------------
  173. // PostCreateViewFromStream is called after subviews are created from resources.  
  174. // Implement initializations for this view that can't be done from a stream.
  175.  
  176. void CFormFrame::PostCreateViewFromStream(Environment* ev)
  177. {
  178.     // ----- Add a ViewTabber to the frame 
  179.     fViewTabber = new FW_CViewTabber(ev, this); 
  180.  
  181.     // ----- Additional processing for non-root (i.e. embedded) frames
  182.     if (IsRoot(ev) == false) 
  183.     {
  184.         // Remove the GrowBox (delete is enough to remove it from the subviews list)
  185.         FW_CView* growBox = FindViewById(ev, kGrowBoxID);
  186.         FW_ASSERT(growBox);
  187.         delete growBox;
  188.         
  189.         // Shrink the scroll-bars to leave space for a 1 pixel border
  190.         // NOTE: It's against OpenDoc HI guidelines to keep scroll-bars in embedded frames
  191.         //         and also to have an embedded frame draw its own border! 
  192.         //         Drawing the border should be the responsability of the container.
  193.         //         ... We wanted this sample to be different.
  194.         
  195.         FW_CView* hSB = FindViewById(ev, kHorzScrollBarID);
  196.         FW_CPoint hSBLoc = hSB->GetLocation(ev);
  197.         hSBLoc.x += FW_kFixedPos1;
  198.         hSBLoc.y -= FW_kFixedPos1;
  199.         FW_CPoint hSBSize = hSB->GetSize(ev);
  200.         hSBSize.x -= FW_IntToFixed(2);
  201.         hSB->SetLocation(ev, hSBLoc, false);
  202.         hSB->SetSize(ev, hSBSize, false);
  203.         
  204.         FW_CView* vSB = FindViewById(ev, kVertScrollBarID);
  205.         FW_CPoint vSBLoc = vSB->GetLocation(ev);
  206.         vSBLoc.x -= FW_kFixedPos1;
  207.         vSBLoc.y += FW_kFixedPos1;
  208.         FW_CPoint vSBSize = vSB->GetSize(ev);
  209.         vSBSize.y -= FW_IntToFixed(2);
  210.         vSB->SetLocation(ev, vSBLoc, false);
  211.         vSB->SetSize(ev, vSBSize, false);
  212.     }
  213. }
  214.  
  215. //----------------------------------------------------------------------------------------
  216. // CFormFrame::CreateSubViews
  217. //----------------------------------------------------------------------------------------
  218. // The subviews created for this frame are:
  219. //
  220. //   CFormFrame 
  221. //         |_____ grow box      (only for root frame)
  222. //         |
  223. //         |_____ horzSB         (horizontal scrollbar)
  224. //         |_____ vertSB         (vertical scrollbar)
  225. //         |
  226. //         |_____ contentView     (scrolling form)
  227. //                      |
  228. //                    |_____ <Several Controls>...
  229. //                                      
  230. void CFormFrame::CreateSubViews(Environment* ev)
  231. {        
  232. #if 1
  233.  
  234.     // WARNING: Make sure that classes created from resources won't be dead-stripped
  235.     FW_DO_NOT_DEAD_STRIP(FW_CGrowBox);
  236.     FW_DO_NOT_DEAD_STRIP(FW_CScrollBarScroller);
  237.     FW_DO_NOT_DEAD_STRIP(FW_CGroupBox);
  238.     FW_DO_NOT_DEAD_STRIP(FW_CScrollBar);
  239.     FW_DO_NOT_DEAD_STRIP(FW_CEditView);
  240.     FW_DO_NOT_DEAD_STRIP(FW_CListBox);
  241.     FW_DO_NOT_DEAD_STRIP(FW_CButton);
  242.     FW_DO_NOT_DEAD_STRIP(FW_CRadioCluster);
  243.     FW_DO_NOT_DEAD_STRIP(FW_CPopupMenu);
  244.  
  245.     CreateSubViewsFromResource(ev, kFormView);
  246.  
  247. #else
  248.     // NOTE: This section is not compiled by default (change #if 1 by #if 0 above
  249.     //        and in FormView.cpp  CFormView::CreateOwnSubViews())
  250.     //         It was left in the sample to show how to create views by program
  251.     //         instead of using resources. See also CFormView::CreateOwnSubViews().
  252.     
  253.     FW_CRect frameRect = GetBounds(ev);  
  254.     FW_CRect contentRect;    // frame rect without scroll-bars
  255.     GetContentRect(ev, contentRect);
  256.     
  257.     if (IsRoot(ev)) 
  258.     {
  259.         // ----- Create the GrowBox only in root frame
  260.         FW_CGrowBox* growBox = new FW_CGrowBox(ev, this, 0, contentRect.BotRight());
  261.     }
  262.     else
  263.     {
  264.         // ----- Leave space to draw 1 pixel border in non-root frames
  265.         frameRect.Inset(FW_kFixedPos1);
  266.     }
  267.  
  268.     // ----- Create the vertical & horizontal scroll bars inside the frame
  269.     FW_CRect vertSbRect(contentRect.right, frameRect.top - FW_kFixedPos1, 
  270.                     frameRect.right + FW_kFixedPos1, contentRect.bottom + FW_kFixedPos1);
  271.     FW_CScrollBar* vertSB = FW_NEW(FW_CScrollBar, (ev, this, 0, vertSbRect));
  272.     
  273.     FW_CRect horzSbRect(frameRect.left - FW_kFixedPos1, contentRect.bottom, 
  274.                     contentRect.right + FW_kFixedPos1, frameRect.bottom + FW_kFixedPos1);
  275.     FW_CScrollBar* horzSB = FW_NEW(FW_CScrollBar, (ev, this, 0,horzSbRect));        
  276.  
  277.     // ----- Create the content view with the extent of the picture
  278.     FW_CRect    pictRect;
  279.     fFormPart->GetPictShape1()->GetRectangle(pictRect);
  280.     CFormView* contentView = new CFormView(ev, this, contentRect, pictRect.Size());
  281.     contentView->MakeContentView(ev); 
  282.     
  283.     // ----- Create the content view's subviews.
  284.     //         Note: We dispatch to a local method instead of creating all subviews here
  285.     //        only for clarity.
  286.     contentView->CreateOwnSubViews(ev);
  287.  
  288.     // ----- Create a scroller  (this must be done AFTER the content view)
  289.     FW_CScroller* scroller = FW_NEW(FW_CScrollBarScroller, (ev, this, horzSB, vertSB));
  290.     AdoptScroller(ev, scroller);
  291.     
  292.     // ----- Add a ViewTabber to the frame 
  293.     fViewTabber = new FW_CViewTabber(ev, this); 
  294. #endif
  295. }
  296.  
  297. //----------------------------------------------------------------------------------------
  298. // CFormFrame::GetContentRect
  299. //----------------------------------------------------------------------------------------
  300.  
  301. void CFormFrame::GetContentRect(Environment* ev, FW_CRect& rect)
  302. {
  303.     rect = GetBounds(ev);
  304.     
  305.     // Leave space to draw 1 pixel border in non-root frames
  306.     if (IsRoot(ev) == FALSE)
  307.         rect.Inset(FW_kFixedPos1);
  308.         
  309.     FW_CPoint sbSize = FW_CScrollBar::GetDefaultScrollBarSize();
  310.  
  311.     rect.right -= sbSize.x;
  312.     rect.bottom -= sbSize.y;
  313. }
  314.  
  315. //----------------------------------------------------------------------------------------
  316. // CFormFrame::Draw
  317. //----------------------------------------------------------------------------------------
  318.  
  319. void CFormFrame::Draw(Environment *ev, ODFacet* odFacet, ODShape* invalidShape)
  320. {
  321.     FW_CRect contentRect;
  322.     GetContentRect(ev, contentRect);
  323.     FW_CRect formRect(GetFrame(ev)->GetContentView(ev)->GetBounds(ev));
  324.     
  325.     FW_CViewContext vc(ev, this, odFacet, invalidShape);
  326.     
  327.     // Draw a gray background around the content view if it's smaller
  328.     if (formRect.right < contentRect.right || formRect.bottom < contentRect.bottom ) {
  329.  
  330.         // Draw outside the center view to avoid flashing 
  331.         FW_CAcquiredODShape aqPaneShape = ::FW_NewODShape(ev, contentRect);
  332.         FW_CAcquiredODShape aqCenterShape = ::FW_NewODShape(ev, formRect);
  333.         aqPaneShape->Subtract(ev, aqCenterShape);
  334.     
  335.         FW_CRegionShape::RenderRegion(vc, aqPaneShape, FW_kFill, FW_kRGBLightGray);
  336.     
  337.         // Draw a shadow frame around the content view
  338.         formRect.Inset(-FW_kFixedPos1,-FW_kFixedPos1);
  339.          FW_CRectShape::RenderRect(vc, formRect, FW_kFrame);
  340.          FW_CPoint p1(formRect.left, formRect.bottom);
  341.          FW_CPoint p2(formRect.right, formRect.bottom);
  342.          FW_CPoint p3(formRect.right, formRect.top);
  343.          if (formRect.bottom < contentRect.bottom - FW_kFixedPos1)
  344.              FW_CLineShape::RenderLine(vc, p1, p2);
  345.         if (formRect.right < contentRect.right - FW_kFixedPos1)
  346.              FW_CLineShape::RenderLine(vc, p2, p3);
  347.     }
  348.     
  349.     if (IsRoot(ev) == FALSE)
  350.     {
  351.         // Draw a 1 pixel border in non-root frames
  352.         FW_CRect frameRect = GetBounds(ev);        
  353.          FW_CRectShape::RenderRect(vc, frameRect, FW_kFrame);
  354.     }
  355. }
  356.  
  357. //----------------------------------------------------------------------------------------
  358. //    CFormFrame::FrameShapeChanged
  359. //----------------------------------------------------------------------------------------
  360.  
  361. void CFormFrame::FrameShapeChanged(Environment *ev)
  362. {
  363.     // By default a FW_CFrame view is not refreshed entirely when resized.
  364.     // You must decide if your content's appearance depends on the frame's size.
  365.     // If it doesn't you don't need to override FrameShapeChanged().
  366.     // Here it's a little bit tricky because the formView is centered inside the frame,
  367.     // so the only case where the content is fixed is when the frame is smaller than the
  368.     // formView, i.e. the formView is anchored to the topleft corner.
  369.  
  370.     // Check if the formView was anchored before resizing
  371.     FW_CRect contentRect;
  372.     GetContentRect(ev, contentRect);
  373.     FW_CSuperView* formView = GetFrame(ev)->GetContentView(ev);
  374.     FW_CPoint formTopLeft(formView->GetBounds(ev).TopLeft());
  375.     FW_Boolean    anchoredBefore = (contentRect.TopLeft() == formTopLeft) ? TRUE : FALSE;    
  376.  
  377.     // Resize the frame (this invalidates only the modified region)
  378.     FW_CFrame::FrameShapeChanged(ev);    
  379.     
  380.     // Check if the formView is anchored after resizing
  381.     GetContentRect(ev, contentRect);
  382.     formTopLeft = formView->GetBounds(ev).TopLeft();
  383.     FW_Boolean    anchoredAfter = (contentRect.TopLeft() == formTopLeft) ? TRUE : FALSE;    
  384.     
  385.     // Invalidate the whole frame if we need redraw outside the formview
  386.     if (!anchoredBefore || !anchoredAfter)
  387.         Invalidate(ev);
  388. }
  389.  
  390. //----------------------------------------------------------------------------------------
  391. //    CFormFrame::HandleNotification
  392. //----------------------------------------------------------------------------------------
  393.  
  394. void CFormFrame::HandleNotification(Environment* ev, const FW_CNotification& notification)
  395. {
  396.     // Handle notification messages for the registered controls.
  397.     // GetMessage() allows to switch on the type of notification.  For each message
  398.     // we can down-cast "notification" directly to the right subclass, without
  399.     // having to use RTTI, because the message is unique to that type.
  400.     
  401.     switch (notification.GetMessage()) 
  402.     {
  403.         case FW_kButtonPressedMsg: 
  404.         {
  405.             const FW_CControlNotification& controlNotification = 
  406.                                                     (FW_CControlNotification&) notification;
  407.  
  408.             // This frame has several active buttons, we must switch on the button id
  409.             ODID viewId = controlNotification.GetViewId(ev);
  410.             switch (viewId) 
  411.             {
  412.                 case kNoRadioID:
  413.                     // Show alert if "No" is being selected... and select "Yes" again :)
  414.                     if (controlNotification.GetControl(ev)->GetValue(ev) == 1)
  415.                     {
  416.                         // FW_Beep();
  417.                         FW_NoteAlert(FW_CString(), FW_CString("  We don't believe you!   :-)"));
  418.                         FW_CButton* yesButton = (FW_CButton*)  FindViewById(ev, kYesRadioID);
  419.                         yesButton->SetValue(ev, 1);
  420.                     }
  421.                     break;
  422.                     
  423.                 case kSubscribeButtonID:
  424.                     // Open modal dialog to pick a password
  425.                     OpenPasswordDialog(ev);
  426.                     break;
  427.                     
  428.                 case kAddButtonID:
  429.                 case kRemoveButtonID:
  430.                     // Add/Remove an item in the list box
  431.                     ModifyPlatformList(ev, viewId == kAddButtonID ? TRUE : FALSE);
  432.                     break;
  433.             }
  434.         } 
  435.         break;
  436.     
  437.         case FW_kPopupClickedMsg: 
  438.         {
  439.             const FW_CPopupMenuNotification& popupNotification = 
  440.                                                 (FW_CPopupMenuNotification&) notification;
  441.             // no need to check ids, there is only 1 active popup (kBrowseTimePopupID)
  442.             // Bring up an alert if the last item is selected
  443.             if (popupNotification.GetMenuIndex(ev) == 4)
  444.             {
  445.                 FW_CString alertString;
  446.                 popupNotification.GetMenuString(ev, alertString);
  447.                 alertString += "... that's too much!"; 
  448.                 FW_NoteAlert(FW_CString(), alertString);
  449.             }
  450.         }
  451.         break;
  452.         
  453.         case FW_kListBoxDoubleClickedMsg:
  454.         {
  455.             const FW_CListBoxNotification& listBoxNotification = 
  456.                                                     (FW_CListBoxNotification&) notification;
  457.             
  458.             // Beep if we double-click on the first item
  459.             if (listBoxNotification.GetListBox(ev)->GetSelectedItem(ev) == 1)
  460.                 FW_Beep();
  461.         }
  462.         break;
  463.         
  464.         default:
  465.             FW_ASSERT("CFormFrame can't respond to this");
  466.             break;
  467.     }
  468. }
  469.  
  470. //----------------------------------------------------------------------------------------
  471. //    CFormFrame::OpenPasswordDialog
  472. //----------------------------------------------------------------------------------------
  473.  
  474. void CFormFrame::OpenPasswordDialog(Environment* ev)
  475. {
  476.     // position is not important here because we use FW_kStandardDialogPosition in the
  477.     // window style to center the dialog properly.
  478.     FW_CPoint    position(FW_kZeroPoint);
  479.     FW_CPoint    size(FW_IntToFixed(300), FW_IntToFixed(160));
  480.     
  481.     FW_WindowStyle style = FW_kStandardDialogPosition | FW_kHasCaption;
  482.  
  483.     FW_CPresentation* dialogPresentation = GetFormPart()->GetPwdDialogPresentation();
  484.     
  485.     // ----- Create new dialog window with the "Password" presentation -----
  486.     //
  487.     // Note: unlike the standard Mac Toolbox "ModalDialog" FW_CDialogFrame::NewModalDialog
  488.     //        returns right after creating the dialog.  Events are processed the same
  489.     //        way as any other OpenDoc frames, except that the modal focus is set.
  490.     //        (NewModalDialog can return NULL if the dialog creation failed because the 
  491.     //        modal focus could not be granted)
  492.     //
  493.     //        Here we use NewModalDialog() instead of NewAndShowModalDialog() because
  494.     //        we need to initialize a few things before opening the dialog
  495.         
  496.     CPwdDialogFrame* dialog = (CPwdDialogFrame*) FW_CDialogFrame::NewModalDialog(ev, 
  497.                             GetPart(ev),                // Your part
  498.                             dialogPresentation,         // Used in CFormPart::NewFrame
  499.                             size,                         // Window size
  500.                             position,                     // Window position
  501.                             style,                        // Make dialog moveable
  502.                             FW_CString("Password"));    // Title for moveable dialog
  503.     
  504.     if (dialog != NULL)
  505.     {                        
  506.         dialog->Initialize(ev, this);        
  507.         dialog->GetWindow(ev)->Show(ev);
  508.     }
  509. }
  510. //----------------------------------------------------------------------------------------
  511. //    CFormFrame::ModifyPlatformList
  512. //----------------------------------------------------------------------------------------
  513.  
  514. void CFormFrame::ModifyPlatformList(Environment* ev, FW_Boolean addItem)
  515. {
  516.     FW_CListBox* list = (FW_CListBox*)FindViewById(ev, kPlatformListBoxID);
  517.     FW_ASSERT(list);
  518.     
  519.     if (addItem)
  520.     {
  521.         // Add a row after the last selected item or at the end.
  522.         short addIndex = 0;
  523.         short count = list->GetSelectionCount(ev);
  524.  
  525.         if (count > 0)
  526.         {
  527.             // We must retrieve the list of selected items in a dynamic array
  528.             short* const indexArray = new short[count];
  529.             
  530.             short read = list->GetSelectedItems(ev, count, indexArray);
  531.             FW_ASSERT(read == count);
  532.             
  533.             addIndex = indexArray[count - 1] + 1;
  534.             
  535.             delete indexArray;
  536.         }
  537.         
  538.         list->AddStringItem(ev, FW_CString("Macintosh"), addIndex);        
  539.         list->ScrollIntoView(ev, addIndex);
  540.     }
  541.     else 
  542.     {
  543.         // Remove the first selected item
  544.         short selectedItem = list->GetSelectedItem(ev);
  545.         if (selectedItem > 0)
  546.             list->DeleteItems(ev, selectedItem);
  547.  
  548.         // Update button's state
  549.         FW_CButton* rmButton = (FW_CButton*)FindViewById(ev, kRemoveButtonID);
  550.         if (list->GetSelectedItem(ev) == 0)
  551.             rmButton->Disable(ev);
  552.     }
  553. }
  554.  
  555.  
  556. //----------------------------------------------------------------------------------------
  557. //    CFormFrame::NewPrintHandler
  558. //----------------------------------------------------------------------------------------
  559. // NewPrintHandler is all you need to support printing!
  560. // (You can also add a progress dialog resource as in ODF Draw's FWPrint.r but it's
  561. //  not necessary and not very useful with background printing)
  562.  
  563. FW_CPrintHandler* CFormFrame::NewPrintHandler(Environment* ev)
  564. {
  565.     // We use the base PrintHandler class for basic printing of the content view
  566.     // (See ODF Draw for a more advanced example)
  567.     return new FW_CPrintHandler(fFormPart, this);
  568. }
  569.  
  570.  
  571. //----------------------------------------------------------------------------------------
  572. //    CFormFrame::NewClipboardCommand
  573. //----------------------------------------------------------------------------------------
  574.  
  575. FW_CClipboardCommand* CFormFrame::NewClipboardCommand(Environment* ev, ODCommandID commandID)
  576. {
  577.     CEditViewCommand* cmd = FW_NEW(CEditViewCommand, (ev, commandID, this, FW_kCanUndo));
  578.     return cmd;
  579. }
  580.  
  581.